第 9 章:引入配置 ConfigMap
要解決的問題
將不同環境(development, staging, production)的部署代碼一併交付在程式碼中,而沒有發覺自己將應用服務暴露在危險之中,一旦其他人也存取代碼,便能知道各個環境的敏感資料
解決方法
將配置環境儲存於環境變數中
,降低 Configuration 與程式碼的耦合,減少環境代碼被交付在程式碼的情況。然而,將環境代碼與程式碼分開之後,我們也會面臨到另外一個問題:環境代碼(Configuration)可能通常都散落在各地,必須有個的地方統一管理這些環境代碼
。更糟的是,每個環境代碼可能都因應不同的程式語言,有著不同的格式,也使得統一管理更加困難。
使用 ConfigMap ,不但提供一個 Configuration 可以統一存放的地方,也提供一個方法讓開發者可以動態且代碼化
的方式為每個應用服務配置其相對應的 Configuration。
什麼是 Configuration
泛指程式存取外部資源或是部署所需的資料
,像是 資料庫的所在 IP、管理者的帳號密碼,或是 Nginx 的設定檔 等等。若是其他人不小心存取資源,導致資料庫被刪除,或被公開,都可能導致專案陷入危險之中,或是損害該應用服務的使用者的權益
ConfigMap 與 Secret 的差別
Secret 與 ConfigMap 想解決的面向不太相同,我們可以將機密的資料存在 Secret 中,且 Secret 會將這些值經過 Base64 加密,機密的資料像是 API 或是 database 的密碼;而將非機密但屬於部署面的資料放在 ConfigMap,好比資料庫的 port number 或是 Redis 的 config file
介紹 ConfigMap
- 一個 ConfigMap 物件可以存入整個 configuration 像是 webserver config file, Nginx config file
- 無需修改 container 程式碼,可以替換不同環境的 Config 開發過程中,常因應不同的環境需配置不同的 configuration,像是
staging
與production
存取的資料庫位址不一致等等。無需修改程式碼的特點,可以幫助我們更快部署到各個不同的環境中 - 統一存放所有的 configuration 透過
kubectl get
指令快速查看目前系統所有的 ConfigMap
典型用法
- 生成為容器內的環境變量
- 設置容器啟動命令的啟動參數 (須設置為環境變量)
- 以 Volume 的形式掛載為容器內部的文件和目錄
使用限制
- 必須在 Pod 之前創建
- 受 namespace 限制
建置 ConfigMap
1. 匯入整個 config 檔
ConfigMap 提供 --from-file
參數,讓我們可以存入整個檔案,以 Redis config file 為例
my-redis.conf
bind 127.0.0.1
port 6379
maxclients 10000
maxmemory 50mb
maxmemory-policy volatile-lru
syslog-enabled yes
dir /var/lib/redis
dbfilename redis.dump.rdb
databases 1
appendfsync everysec
save 600 10
透過 kubectl create
創建,
kubectl create configmap redis-config --from-file=my-redis.conf
configmap "redis-config" created
kubectl get configmap
NAME DATA AGE
kube-root-ca.crt 1 6h19m
redis-config 1 84s
可以看到我們剛剛創建好的 redis-config
,如果用 kubectl describe
可以看到該物件詳細資訊
kubectl describe
kubectl describe configmap redis-config
Name: redis-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
my-redis.conf:
----
bind 127.0.0.1
port 6379
maxclients 10000
maxmemory 50mb
maxmemory-policy volatile-lru
syslog-enabled yes
dir /var/lib/redis
dbfilename redis.dump.rdb
databases 1
appendfsync everysec
save 600 10
BinaryData
====
Events: <none>
2. 透過指令設定 Config
而我們也可以在 kubectl create
指令中,直接設定 ConfigMap 的值
kubectl create
kubectl create configmap mysql-host --from-literal=ip=127.0.0.1
configmap "mysql-host" created
kubectl describe configmap mysql-host
Name: mysql-host
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
ip:
----
127.0.0.1
BinaryData
====
Events: <none>
移除掉某個特定的 ConfigMap 物件,可以使用 kubectl delete
來刪除
kubectl delete configmap mysql-host
configmap "mysql-host" deleted
- 匯入整個 YAML 檔案
Config YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: appconfig
data:
bind: 127.0.0.1
port: 6379
maxclients: 10000
maxmemory: 50mb
maxmemory-policy: volatile-lru
syslog-enabled: yes
dir: /var/lib/redis
dbfilename: redis.dump.rdb
databases: 1
appendfsync: everysec
save: 600 10
使用 ConfigMaps
Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
- name: MYSQL_USER
valueFrom:
configMapKeyRef:
name: mysql-secret
key: MYSQL_USER
- name: MYSQL_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
使用 envFrom
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
envFrom:
- configMapRef:
name: appconfig
Volumes
apiVersion: v1
kind: Pod
metadata:
name: pod-env
spec:
volumes:
- name: appconfig
configMap:
name: appconfig
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "while true; do echo $(date) >> /tmp/index.html; sleep 10; done"]
volumeMounts:
- name: appconfig
mountPath: "/etc/appconfig"
實作:如何透過 ConfigMap 配置 Nginx
什麼是 Nginx
Nginx 不只是一套輕量的 HTTP 伺服器,同時也是一個反向代理的伺服器。收到用戶的請求後,可以將流量導給後端的 service,再將後端處理好的資源回傳給前端使用者。
Nginx 配置檔案
- my-pod.yaml
kubectl config view
apiVersion: v1
kind: Pod
metadata:
name: apiserver
labels:
app: webserver
tier: backend
spec:
containers:
- name: nodejs-app
image: zxcvbnius/docker-demo
ports:
- containerPort: 3000
- name: nginx
image: nginx:1.13
ports:
- containerPort: 80
volumeMounts:
- name: nginx-conf-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-conf-volume
configMap:
name: nginx-conf
items:
- key: my-nginx.conf
path: my-nginx.conf
my-nginx.conf
server {
listen 80;
server_name localhost;
location / {
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
建立 ConfigMap 與 Pod
kubectl create configmap nginx-conf --from-file=my-nginx.conf
configmap/nginx-conf created
kubectl create -f my-pod.yaml
pod/my-pod created
Nginx 與 API server 的配置
kubectl get pods
NAME READY STATUS RESTARTS AGE
apiserver 2/2 Running 0 17s
確定狀態後,使用 kubectl expose
,指定將 port 80 對應到 minikube 上的某一個 port,
$ kubectl expose pod apiserver --port=80 --type=NodePort
service "apiserver" exposed